package com.china.cnlongdb.server.config;

import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.List;

import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.alibaba.fastjson.JSON;
import com.china.cnlongdb.basic.config.LongDbConfig;
import com.china.cnlongdb.basic.constant.LongDbConstant;
import com.china.cnlongdb.basic.entity.database.LongDataBaseEntity;
import com.china.cnlongdb.basic.entity.matchingrule.MatchingDataTime;
import com.china.cnlongdb.basic.entity.matchingrule.MatchingField;
import com.china.cnlongdb.basic.entity.matchingrule.MatchingMasterSlave;
import com.china.cnlongdb.basic.entity.matchingrule.MatchingReadWrite;
import com.china.cnlongdb.basic.enums.LongdbConfigEnums;
import com.china.cnlongdb.basic.utils.DealStrSubUtils;
import com.china.cnlongdb.basic.utils.LongFileOperator;
import com.china.cnlongdb.server.connect.LongDbConnect;

/**
 * 系统名称：LongdbConfig
 * 模块名称：
 × 模块描述：longdb - 配置操作处理类
 × 功能列表：
 × 模块作者：ZOUYONG
 × 开发时间：2018年12月23日 下午12:38:41
 × 模块路径：com.china.cnlongdb.server.config.LongdbConfigHandler
 */
public class LongdbConfigHandler {

	private static final Logger logger = (Logger) LoggerFactory.getLogger(LongdbConfigHandler.class);

	/**
	 * 功能描述：获取数据库总的配置信息
	 * 模块作者：ZOUYONG
	 * 开发时间：2019年4月1日 下午9:59:31
	 * 更新记录：
	 * 返回数据：LongDbConnect
	 */
	public static LongDbConnect getLongdbConnect(File cfgFile) {
		String dataBaseRootDirectory = null, dataBaseName = null, dataBaseIp = null;
		Integer dataBasePort = null;
		// 获取总配置文件的内容
		BufferedReader bufReader = null;
		BufferedInputStream bis = null;
		try {
			// FileReader:用来读取字符文件的便捷类。
			bis = new BufferedInputStream(new FileInputStream(cfgFile));
			bufReader = new BufferedReader(
				new InputStreamReader(bis, LongDbConstant.longdbSystemCharset), LongDbConfig.longdbTableFileBufferedReaderSize
			);
			String content = null;
			while ((content = bufReader.readLine()) != null) {
				if(StringUtils.isBlank(content) || content.startsWith(LongDbConstant.longdbTableDataTranD)) {
					continue;
				}
				String[] cfgAry = content.split(LongDbConstant.longdbTableDataTranB);
				if(cfgAry.length <= 1) {
					continue;
				}
				String cfgName = cfgAry[0];
				if(StringUtils.isBlank(cfgName)) {
					continue;
				}
				if(cfgName.equals(LongdbConfigEnums.dataBaseRootDirectory.name())) {
					dataBaseRootDirectory = replaceSeparator(cfgAry[1]);
				}
				else if(cfgName.equals(LongdbConfigEnums.dataBaseName.name())) {
					dataBaseName = cfgAry[1];
				}
				else if(cfgName.equals(LongdbConfigEnums.dataBaseIp.name())) {
					dataBaseIp = cfgAry[1];
				}
				else if(cfgName.equals(LongdbConfigEnums.dataBasePort.name())) {
					dataBasePort = Integer.valueOf(cfgAry[1]);
				}
			}
		}
		catch (Exception e) {
			logger.error(e.getMessage(), e);
			throw new RuntimeException(e);
		}
		finally {
			if (bufReader != null) {
				try {
					bufReader.close();
				} catch (IOException e) {
					logger.error(e.getMessage(), e);
					bufReader = null;
				}
			}
			if(null != bis) {
				try {
					bis.close();
				} catch (IOException e) {
					logger.error(e.getMessage(), e);
					bis = null;
				}
			}
		}
		return new LongDbConnect(dataBaseRootDirectory, dataBaseName, dataBaseIp, dataBasePort);
	}
	
	/**
	 * 功能描述：解析数据库表分区等配置信息
	 × 模块作者：ZOUYONG
	 × 开发时间：2018年12月23日 下午12:38:22
	 × 返回数据：void
	 */
	public static void initLongdbRuleData(List<File> ruleList, LongDbConnect connect) {
		if(CollectionUtils.isEmpty(ruleList)) {
			logger.info("初始化数据表分区配置信息为空!");
			return;
		}
		if(null == connect || StringUtils.isBlank(connect.getDataBaseName())) {
			return;
		}
		LongDataBaseEntity dataBase = connect.getCreateOperator().getLongDataBaseEntity(connect.getDataBaseName());
		if(null == dataBase) {
			return;
		}
		// 创建表分区的配置文件
		File dbRule = new File(connect.getDataBaseRootDirectory() + dataBase.getFilePath() + File.separator + connect.getDataBaseName() + LongDbConstant.longdbRuleSuffix);
		if(!dbRule.exists()) {
			throw new RuntimeException("未找到数据库" + connect.getDataBaseName() + "的配置文件!");
		}
		
		List<Object> matchingList = new ArrayList<Object>();
		for (File file : ruleList) {
			// 读取文件内容
			BufferedReader bufReader = null;
			BufferedInputStream bis = null;
			try {
				// FileReader:用来读取字符文件的便捷类。
				bis = new BufferedInputStream(new FileInputStream(file));
				bufReader = new BufferedReader(
					new InputStreamReader(bis, LongDbConstant.longdbSystemCharset), 
					LongDbConfig.longdbTableFileBufferedReaderSize
				);
				String content = null;
				while ((content = bufReader.readLine()) != null) {
					if(StringUtils.isBlank(content) || content.startsWith(LongDbConstant.longdbTableDataTranD)) {
						continue;
					}
					try {
						matchingList.addAll(getMatchingRuleData(content, connect));
					} catch(Exception e) {
						throw new RuntimeException(file.getName() + e.getMessage());
					}
				}
			}
			catch (Exception e) {
				logger.error(e.getMessage(), e);
			}
			finally {
				if (bufReader != null) {
					try {
						bufReader.close();
					} catch (IOException e) {
						logger.error(e.getMessage(), e);
						bufReader = null;
					}
				}
				if(null != bis) {
					try {
						bis.close();
					} catch (IOException e) {
						logger.error(e.getMessage(), e);
						bis = null;
					}
				}
			}
		}
		if(CollectionUtils.isEmpty(matchingList)) {
			return;
		}
		StringBuffer matchStr = new StringBuffer();
		for (Object matching : matchingList) {
			matchStr.append(JSON.toJSONString(matching)).append(LongDbConstant.longdbTableChangeLine);
		}
		connect.setMatchingList(matchingList);
		LongFileOperator.writeFileByStream(dbRule, matchStr.toString());
	}

	/**
	 * 功能描述：获取表分区配置文件
	 × 模块作者：ZOUYONG
	 × 开发时间：2018年12月23日 下午12:49:25
	 × 返回数据：MatchingRuleEntity
	 */
	private static List<Object> getMatchingRuleData(String content, LongDbConnect connect) {
		
		List<Object> matchingList = new ArrayList<Object>();
		String rgex = "'(.*?)'"; // 获取两个单引号中间的内容
		String rule = DealStrSubUtils.getSubUtilSimple(content, rgex);
		if(StringUtils.isBlank(rule)) {
			return matchingList;
		}
		
		MatchingField field = null;
		String[] ary = rule.split(";");
		for (int i=0; i<ary.length; i++) {
			String string = ary[i];
			String[] temp = string.split(":");
			// 表字段分区配置
			if(content.startsWith(LongDbConstant.fieldMatching)) {
				field = matchingForeignValue(rule, field);
				break;
			}
			// 表时间分区配置
			else if(content.startsWith(LongDbConstant.dateTimeMatching)) {
				matchingList.addAll(matchingDateTimeValue(rule));
			}
			// 读写分离-读配置
			else if(content.startsWith(LongDbConstant.longdbReadConnect)) {
				MatchingReadWrite readWrite = new MatchingReadWrite();
				readWrite.setRuleType(LongDbConstant.longdbReadConnect);
				readWrite.setDataBaseIp(temp[0]);
				readWrite.setDataBasePort(Integer.valueOf(temp[1]));
				matchingList.add(readWrite);
			}
			// 读写分离-写配置
			else if(content.startsWith(LongDbConstant.longdbWriteConnect)) {
				MatchingReadWrite readWrite = new MatchingReadWrite();
				readWrite.setRuleType(LongDbConstant.longdbWriteConnect);
				readWrite.setDataBaseIp(temp[0]);
				readWrite.setDataBasePort(Integer.valueOf(temp[1]));
				matchingList.add(readWrite);
			}
			// 主从备份配置
			else if(content.startsWith(LongDbConstant.longdbSlaveDataBase)) {
				MatchingMasterSlave masterSlave = new MatchingMasterSlave();
				masterSlave.setRuleType(LongDbConstant.longdbSlaveDataBase);
				masterSlave.setDataBaseIp(temp[0]);
				masterSlave.setDataBasePort(Integer.valueOf(temp[1]));
				matchingList.add(masterSlave);
			}
		}
		if(null != field) {
			matchingList.add(field);
		}
		return matchingList;
	}

	/**
	 * 功能描述：表主外键分区拆分配置信息
	 × 模块作者：ZOUYONG
	 × 开发时间：2019年4月15日 下午12:25:58
	 × 返回数据：MatchingForeignKey
	 */
	private static MatchingField matchingForeignValue(String rule, MatchingField field) {
		if(StringUtils.isBlank(rule)) {
			return null;
		}
		if(null == field) {
			field = new MatchingField();
		}
		field.setRuleType(LongDbConstant.fieldMatching);
		String[] ary = rule.split(";");
		for (int i=0; i<ary.length; i++) {
			String string = ary[i];
			if(StringUtils.isBlank(string)) {
				continue;
			}
			String[] temp = string.split(":");
			if(temp.length <= 1 || StringUtils.isBlank(temp[0]) 
					|| StringUtils.isBlank(temp[1])) {
				continue;
			}
			List<String> fids = field.getFieldMap().get(temp[0]);
			if(null == fids) {
				fids = new ArrayList<String>();
			}
			String[] fdary = temp[1].split("#");
			for (String fd : fdary) {
				if(StringUtils.isBlank(fd) || fids.contains(fd)) {
					continue;
				}
				fids.add(fd);
			}
			field.getFieldMap().put(temp[0], fids);
		}
		return field;
	}

	/**
	 * 功能描述：表时间分区配置
	 × 模块作者：ZOUYONG
	 × 开发时间：2019年4月15日 下午1:52:52
	 × 返回数据：MatchingDataTime
	 */
	private static List<MatchingDataTime> matchingDateTimeValue(String rule) {
		List<MatchingDataTime> dataTimeList = new ArrayList<MatchingDataTime>();
		if(StringUtils.isBlank(rule)) {
			return dataTimeList;
		}
		String[] ary = rule.split(";");
		for (int i=0; i<ary.length; i++) {
			String string = ary[i];
			if(StringUtils.isBlank(string)) {
				continue;
			}
			String[] temp = string.split(":");
			if(temp.length < 2) {
				continue;
			}
			String nameStr = temp[0];
			boolean isDataBase = nameStr.startsWith(LongDbConstant.longdbDataBase);
			String[] dataBaseAry = nameStr.split(LongDbConstant.longdbTableDataTranB);
			String splitStr = null;
			if(dataBaseAry.length > 1) {
				splitStr = dataBaseAry[1];
			} else {
				splitStr = nameStr;
			}
			String[] names = splitStr.split(LongDbConstant.longdbTableDataTranD);
			for (String name : names) {
				MatchingDataTime dataTime = new MatchingDataTime();
				if(isDataBase) {
					dataTime.setRelationDataBase(name);
				} else {
					dataTime.setRelationTableName(name);
				}
				dataTime.setTimePartition(temp[1]);
				dataTime.setRuleType(LongDbConstant.dateTimeMatching);
				dataTimeList.add(dataTime);
			}
		}
		return dataTimeList;
	}

	/**
	 * 功能描述：获取指定表名的分区关系数据
	 × 模块作者：ZOUYONG
	 × 开发时间：2018年12月23日 下午1:35:36
	 × 返回数据：List<MatchingRuleEntity>
	 */
	public static List<Object> getMatchingRuleListByTableName(LongDbConnect connect, String tableName) {
		List<Object> tempList = new ArrayList<Object>();
		if(StringUtils.isBlank(tableName)) {
			return tempList;
		}
		
		// 从内存中获取配置数据
		List<Object> mathcList = connect.getMatchingList();
		for (int i=0; i<mathcList.size(); i++) {
			Object matching = mathcList.get(i);
			if(matching instanceof MatchingField) {
				MatchingField field = (MatchingField) matching;
				if(null != field.getFieldMap().get(tableName) && 
						field.getFieldMap().get(tableName).size() > 0) {
					tempList.add(field);
				}
			}
			else if(matching instanceof MatchingDataTime) {
				MatchingDataTime dateTime = (MatchingDataTime) matching;
				if(tableName.equals(dateTime.getRelationTableName()) 
						|| connect.getDataBaseName().equals(dateTime.getRelationDataBase())) {
					tempList.add(dateTime);
				}
			}
		}
		return tempList;
	}
	
	/**
	 * 功能描述：获取读/写数据库列表
	 × 模块作者：ZOUYONG
	 × 开发时间：2018年12月24日 下午1:57:10
	 × 返回数据：List<MatchingRuleEntity>
	 */
	public static List<MatchingReadWrite> getMatchingRuleListByConnect(LongDbConnect connect, String ruleType) {
		if(StringUtils.isBlank(ruleType)) {
			return null;
		}
		List<Object> mathcList = connect.getMatchingList();	// 从内存中获取配置数据
		List<MatchingReadWrite> tempList = new ArrayList<MatchingReadWrite>();
		for (Object matching : mathcList) {
			if(matching instanceof MatchingReadWrite) {
				MatchingReadWrite readWrite = (MatchingReadWrite) matching;
				if(ruleType.equals(readWrite.getRuleType())) {
					tempList.add(readWrite);
				}
			}
		}
		return tempList;
	}
	
	/**
	 * 功能描述：处理分隔符
	 × 模块作者：ZOUYONG
	 × 开发时间：2019年4月16日 上午10:11:49
	 × 返回数据：String
	 */
	private static String replaceSeparator(String text) {
		if(StringUtils.isBlank(text)) {
			return "";
		}
		
		String start = "";
		String[] ary = null;
		if(text.indexOf("/") >= 0) {
			start = "/";
			ary = text.split("/");
		}
		else if(text.indexOf("\\") >= 0) {
			ary = text.split("\\\\");
		}
		if(null == ary) {
			return "";
		}

		StringBuffer msg = new StringBuffer(start);
		for (String str : ary) {
			if(StringUtils.isBlank(str)) {
				continue;
			}
			msg.append(str).append(File.separator);
		}
		return msg.toString();
	}

}
